package org.liuyuantao.client;

import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import javax.ws.rs.client.AsyncInvoker;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.glassfish.jersey.apache.connector.ApacheClientProperties;
import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.springframework.util.CollectionUtils;

/**
 * 客户端发送Rest请求工具
 *
 * @author
 * @since
 */
public class RestRequest<R, E> {
	private final Logger log = LogManager.getLogger(RestRequest.class.getName());
	private static final int MAX_LISTENERS = 5;
	private static final int MAX_ITEMS = 10;
	public static final String GET = "GET";
	public static final String DELETE = "DELETE";
	public static final String PUT = "PUT";
	public static final String POST = "POST";
	private ClientConfig clientConfig;
	private boolean isAsync = false;
	private Set<Class<?>> clientRegisters;

	public RestRequest() {
	}

	public RestRequest(final ClientConfig clientConfig) {
		this.clientConfig = clientConfig;
	}

	public void useApacheConnector() {
		PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
		cm.setMaxTotal(MAX_LISTENERS * MAX_ITEMS);
		cm.setDefaultMaxPerRoute(MAX_LISTENERS * MAX_ITEMS);
		clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, cm);
		clientConfig.connectorProvider(new ApacheConnectorProvider());
	}

	public void setAsync(boolean isAsync) {
		this.isAsync = isAsync;
	}

	public void setClientRegisters(final Set<Class<?>> clientRegisters) {
		this.clientRegisters = clientRegisters;
	}

	public E rest(final String method, final String requestUrl, final Class<E> returnType) {
		return rest(method, requestUrl, null, null, null, null, returnType);
	}

	public E rest(final String method, final String requestUrl, final Set<RestRequestParam> headParams,
			final Set<RestRequestParam> queryParams, final MediaType requestDataType, final Class<E> returnType) {
		return rest(method, requestUrl, headParams, queryParams, requestDataType, null, returnType);
	}

	public E rest(final String method, final String requestUrl, final MediaType requestDataType, final R requestData,
			final Class<E> returnType) {
		return rest(method, requestUrl, null, null, requestDataType, requestData, returnType);
	}

	/**
	 * 
	 * @param method
	 *            HTTP请求方法名
	 * @param requestUrl
	 *            请求资源地址
	 * @param headParams
	 *            请求头参数
	 * @param queryParams
	 *            请求查询参数
	 * @param requestDataType
	 *            请求媒体类型
	 * @param requestData
	 *            返回值类型
	 * @param returnType
	 *            请求实体信息
	 * @return
	 */
	public E rest(final String method, final String requestUrl, final Set<RestRequestParam> headParams,
			final Set<RestRequestParam> queryParams, final MediaType requestDataType, final R requestData,
			final Class<E> returnType) {
		// 构造Client
		if (clientConfig == null) {
			clientConfig = new ClientConfig();
		}
		final Client client = ClientBuilder.newClient(clientConfig);

		if (!CollectionUtils.isEmpty(clientRegisters)) {
			for (final Class<?> clazz : clientRegisters) {
				client.register(clazz);
			}
		}
		// 构造WebTarget
		WebTarget webTarget = client.target(requestUrl);
		if (!CollectionUtils.isEmpty(queryParams)) {
			for (final RestRequestParam atupRequestParam : queryParams) {
				webTarget = webTarget.queryParam(atupRequestParam.getKey(), atupRequestParam.getValue());
			}
		}
		// 构造Invocation.Builder
		final Invocation.Builder invocationBuilder = webTarget.request();
		if (!CollectionUtils.isEmpty(headParams)) {
			for (final RestRequestParam atupRequestParam : headParams) {
				invocationBuilder.header(atupRequestParam.getKey(), atupRequestParam.getValue());
			}
		}

		Response response = null;
		Entity<R> entity;
		switch (method) {
		case GET:
			response = invocationBuilder.get();
			break;
		case DELETE:
			response = invocationBuilder.delete();
			break;
		case PUT:
			entity = Entity.entity(requestData, requestDataType);
			response = invocationBuilder.put(entity);
			break;
		case POST:
			if (isAsync) {
				final AsyncInvoker async = invocationBuilder.async();
				entity = Entity.entity(requestData, requestDataType);
				final Future<E> responseFuture = async.post(entity, returnType);
				try {
					return responseFuture.get(RestVariable.ASYNC_LAUNCH_TEST_TIMEOUT + 2, TimeUnit.SECONDS);
				} catch (Exception e) {
					log.error(e);
				}
				break;
			} else {
				entity = Entity.entity(requestData, requestDataType);
				response = invocationBuilder.post(entity);
				break;
			}
		default:
			response = invocationBuilder.get();
		}
		if (response != null) {
			return response.readEntity(returnType);
		} else {
			client.close();
			return null;
		}
	}

	// 客户端代理的配置
	public void proxy(String proxyUri, String proxyUserName, String proxyPassword) {
		if (this.clientConfig == null) {
			this.clientConfig = new ClientConfig();
		}
		this.clientConfig.property(ClientProperties.PROXY_URI, proxyUri);
		this.clientConfig.property(ClientProperties.PROXY_USERNAME, proxyUserName);
		this.clientConfig.property(ClientProperties.PROXY_PASSWORD, proxyPassword);
	}

	// 客户端请求超时处理
	public void timeout(int connectTimeout, int readTimeout) {
		if (this.clientConfig == null) {
			this.clientConfig = new ClientConfig();
		}
		if (connectTimeout > 0) {
			this.clientConfig.property(ClientProperties.CONNECT_TIMEOUT, connectTimeout);
		}
		if (readTimeout > 0) {
			this.clientConfig.property(ClientProperties.READ_TIMEOUT, readTimeout);
		}
	}
}
